package com.lx.boot.config;

import com.lx.annotation.Note;
import com.lx.boot.OS;
import com.lx.boot.config.annotation.Properties;
import com.lx.boot.config.annotation.PropertiesField;
import com.lx.entity.ExpireCache;
import com.lx.util.LX;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Component
@Note("配置文件处理器, 实体可以使用@Properties注解")
public class FirmPropertiesUtil implements ApplicationListener<EnvironmentChangeEvent> {

    private static final String PERIOD = ".";

    @Note("缓存系统配置,提高效率")
    private static ExpireCache<String,Object> expireCache = new ExpireCache<String,Object>();

    @Override
    @Note("更新系统参数后清理缓存中的配置!")
    public void onApplicationEvent(EnvironmentChangeEvent event) {
        refreshAll();
    }

    @Note("清理缓存全部配置")
    public static void refreshAll(){
        expireCache.clear();
    }
    @Note("对配置进行缓存")
    public static String getProperty(String key){
        String md5 = LX.md5(OS.getFirmId()+PERIOD+key);
        return (String) expireCache.get(md5, ()->{
            return getProperties(key,String.class);
        });
    }

    @Note("对配置进行缓存")
    public static <T>T getProperty(String key,Class<T> tClass){
        String md5 = LX.md5(OS.getFirmId()+PERIOD+key);
        return (T) expireCache.get(md5, ()->{
            return getProperties(key,tClass);
        });
    }

    @Note("传入一个实体类型,获取配置信息")
    public static <T>T getBeanProperty(Class<T> t){
        return getBeanProperty(t,null);
    }

    @Note("传入一个实体类型,获取配置信息")
    public static <T>T getBeanProperty(Class<T> t, String prefix){
        String md5 = LX.md5(OS.getFirmId()+PERIOD+t.getName()+prefix);
        return (T) expireCache.get(md5, ()->{
            try {
                return getProperties(t.newInstance(),prefix);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    @Note("更新配置类属性")
    public static <T>T getBeanProperty(T bean){
        return getBeanProperty(bean,null);
    }

    @Note("更新配置类属性")
    public static <T>T getBeanProperty(T bean, String prefix){
        Class<?> aClass = bean.getClass();
        String md5 = LX.md5(OS.getFirmId()+PERIOD+bean.getClass().getName()+prefix);
        return (T) expireCache.get(md5, ()->{
            return getProperties(bean,prefix);
        });
    }


    @Note("获取配置文件中配置, 优先获取firmid.key 没有获取 key对应的配置信息")
    static <T>T getProperties(String key, Class<T> type){
        if (OS.getEnvironment() == null){
            log.error("请使用springboot启动后调用!");
            return null;
        }
        return LX.ifNull(OS.getEnvironment().getProperty(OS.getFirmId()+PERIOD+key,type),()->{return OS.getEnvironment().getProperty(key,type);});
    }

    @Note("更新配置类属性")
    static <T>T getProperties(T bean, String prefix){
        if (OS.getEnvironment() == null){
            log.error("请使用springboot启动后调用!");
            return bean;
        }
        Class<?> aClass = bean.getClass();
        if (prefix == null){
            //获取类上的注解信息
            Properties annotation = aClass.getAnnotation(Properties.class);
            if (annotation == null){
                log.error("请传入前缀或在配置类上配置@Properties注解:{}",aClass.getName());
                return bean;
            }
            prefix = annotation.prefix();
        }
        if (!prefix.endsWith(PERIOD)){
            prefix+=PERIOD;
        }
        if (!isBeanClass(aClass)){
            log.error("请传入初始化后的实体类!");
            return bean;
        }
        for (Field field : getFields(aClass)) {
            //获取字段上的注解
            PropertiesField fieldAnnotion = field.getAnnotation(PropertiesField.class);
            String fieldName = field.getName();
            Class<?> type = field.getType();
            String key;
            if (fieldAnnotion == null){
                //注解不存在key为类上的注解+字段名
                key = prefix+fieldName;
            }else{
                if (!fieldAnnotion.value()){
                    //属性上设置不进行处理
                    continue;
                }
                //字段上存在注解则取字段上的key
                key = fieldAnnotion.key();
                if (LX.isEmpty(key)){
                    //没有自定义key时
                    String suffix = fieldAnnotion.suffix();
                    LX.exObj(suffix,aClass.getName()+PERIOD+fieldName+"的自定义customKey或suffix信息不能为空!");
                    key = prefix+suffix;
                }
            }
            Object val = getProperties(key, type);
            try {
                log.debug(aClass.getName()+" "+key+" "+val);
                if (val != null && !Modifier.isFinal(field.getModifiers())){
                    //没有final修饰的字段
                    field.setAccessible(true);
                    field.set(bean,val);
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return bean;
    }

    /**
     * 获取所有字段
     * @param clazz
     * @return
     */
    static Field[] getFields(Class clazz){
        List<Field> fieldList = new ArrayList<>(16);
        while (clazz != null){
            Field[] fields = clazz.getDeclaredFields();
            fieldList.addAll(Arrays.asList(fields));
            clazz = clazz.getSuperclass();
        }
        Field[] f = new Field[fieldList.size()];
        return fieldList.toArray(f);
    }

    private static boolean isBeanClass(Class<?> cls){
        return !Modifier.isAbstract(cls.getModifiers()) && !cls.isEnum() && !cls.isInterface() && !cls.isAnnotation();
    }

}
